#include <iostream>
#include <string.h>
#include <bits/stdc++.h>
#include "httpd.h"

using namespace std;

unsigned short PORT = 8080;
const char *FileSystemPath = "/var/lib/www/htdocs";
#define SERV_IP "127.0.0.1"
#define SERV_PORT 8080
void sendhtml(int cfd, const char *filepath);
const int N = 655360 * 4; // 问题果然出自这里
char buffer2[N * 25];
char readBuf[N * 25]; // 读图片的缓存
// char msg[N*25]; // 消息

// 获取服务端的sfd字段
int get_sfd()
{
	int sfd, ret;
	struct sockaddr_in serv_addr;

	/*创建一个socket TCP协议*/
	sfd = socket(AF_INET, SOCK_STREAM, 0);

	//设置接收地址和端口重用
	int opt = 5;
	setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
	setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));

	/*初始化一个地址结构 man 7 ip 查看对应信息*/
	bzero(&serv_addr, sizeof(serv_addr));		   //将整个结构体清零
	serv_addr.sin_family = AF_INET;				   //选择协议族为IPv4
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); //监听本地所有IP地址，直接是数值
	serv_addr.sin_port = htons(SERV_PORT);		   //绑定端口号，把本地字节序转网络字节序

	/*绑定服务器地址结构*/
	ret = bind(sfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
	if (ret != 0)
	{
		close(sfd);
		printf("bind error:%s\n", strerror(errno));
		exit(-2);
	}

	/*设定链接上限,注意此处不阻塞，等相应*/
	ret = listen(sfd, 5); //同一时刻允许向服务器发起链接请求的数量
	if (ret != 0)
	{
		close(sfd);
		printf("bind error:%s\n", strerror(errno));
		exit(-2);
	}
	printf("wait for client connect ...\n");

	return sfd;
}

void notfind(int cfd)
{
	/*
	 发送404错误 http/1.0 404 not found
	*/
	char sendBuf[N];
	sprintf(sendBuf, "HTTP/1.0 404 NOT FOUND\r\n");
	send(cfd, sendBuf, strlen(sendBuf), 0);
	sprintf(sendBuf, "Content-type:text/html\r\n");
	send(cfd, sendBuf, strlen(sendBuf), 0);
}

// 多线程容易出问题
void *accept_request(void *parameter)
{
	// parameter貌似没有值
	Parameter *param = (Parameter *)parameter;
	int cfd = param->client_socket;
	string webpath = param->client_addr;
	cerr << "[accept_request] cfd = " << cfd << "  [accept_request] webpath = " << webpath << endl;
	//给客户端发送网页
	char filepath[N] = "";
	//获取网页
	strncpy(filepath, webpath.c_str(), webpath.length());

	//判断文件是否存在
	if (access(filepath, 0) == 0)
	{ // 0 判断文件是否存在
		//发送网页
		sendhtml(cfd, filepath);
		cout << "Sendhtml Successfully!" << endl;
	}
	else
	{
		notfind(cfd);
		cout << "No Find!" << endl;
	}
	// close(cfd);
	return NULL;
}

void createResponse(int mode, string& responseStr)
{
	responseStr.clear();
	responseStr.assign("HTTP/1.1 ");
	puts("createResponse!");
	switch(mode)
	{
		case MALFORMED:
			responseStr.append("400 Client Error\r\n");
			break;
		case OK:
			responseStr.append("200 OK\r\n");
			break;
		case FORBIDDEN:
			responseStr.append("403 FORBIDDEN\r\n");
			break;
		case NOTFOUND:
			responseStr.append("404 Not Found\r\n");
			break;
	}
	responseStr.append("Server: Apache\r\n\r\n");
}

// 没用到
char *read_image(const string &image_path, int &flength)
{
	cout << image_path << endl;
	ifstream is(image_path.c_str(), ifstream::in);
	is.seekg(0, is.end);
	flength = is.tellg();
	is.seekg(0, is.beg);
	// char * buffer = new char[flength];
	is.read(readBuf, flength);

	return readBuf;
}

// 没用到
int send_image(int &fd, char *image, int flen)
{
	char msg[65536] = "HTTP/1.1 200 OK\nContent-Encoding:UTF-8\nContent-Type:image/png\n\n";

	int msglen = strlen(msg);
	memcpy(msg + msglen, image, flen);

	// for (int i = 0; i < flen + msglen; ++i) {
	// 	printf("%d ", msg[i]);
	// }
	// printf("\n");

	int ret = write(fd, msg, msglen + flen);
	return ret;
}

//发送图片
int sendPic(int pFD, const char *filepath)
{
	int fd = pFD;
	FILE *fp;
	int len;

	// char readBuf[600*1024] = {0}; //600k
	cout << "sendPic ing!" << endl;
	int tlen;
	char *p_bufs = NULL;

	/* read data */
	fp = fopen(filepath, "rb");
	if (NULL == fp)
	{
		return -1;
	}
	fseek(fp, 0, SEEK_END);

	len = ftell(fp);
	if (len <= 0)
	{
		fclose(fp);
		return -1;
	}
	fseek(fp, 0, SEEK_SET);

	len = fread(readBuf, 1, len, fp);
	fclose(fp);

	/* send data */
	p_bufs = (char *)malloc(len + 1024);
	if (NULL == p_bufs)
	{
		printf("malloc error!\n");
		return -1;
	}

	tlen = sprintf(p_bufs, "HTTP/1.1 200 OK\r\n"
						   "Server: hsoap/2.8\r\n"
						   "Content-Type: image/jpeg\r\n"
						   "Content-Length: %d\r\n"
						   "Connection: close\r\n\r\n",
				   len);

	memcpy(p_bufs + tlen, readBuf, len);
	tlen += len;

	send(fd, p_bufs, tlen, 0);

	free(p_bufs);

	return 0;
}

// 发送网页--最核心功能
void sendhtml(int cfd, const char *filepath)
{
	cout << "filepath in sendhtml: " << filepath << endl;
	if (filepath[strlen(filepath) - 1] != 'g' && filepath[strlen(filepath) - 1] != 'i')
	{
		FILE *pr = fopen(filepath, "r");
		if (pr == NULL)
		{
			close(cfd);
			printf("filepath error:%s\n", strerror(errno));
			exit(-2);
		}
		char data[N] = "";
		cerr << "########################## [sendhtml] ##################################" << endl;

		// 出现问题，图片传不过去
		char sendBuf[N];
		sprintf(sendBuf, "HTTP/1.1 201 OK\nContent-Encoding:UTF-8\r\n");
		send(cfd, sendBuf, strlen(sendBuf), 0);
		int len = strlen(filepath);
		// if(filepath[len -1] == 'g'){
		// 	fclose(pr);
		// 	return;
		// }
		// do{
		// 	fgets(data, 1024, pr);//只读一次，读完需要使用循环
		// 	send(cfd, data, strlen(data), 0);
		// }while (!feof(pr));
		ifstream file;
		// 打开文件
		file.open(filepath, ifstream::in | ios::binary);
		// 计算长度
		file.seekg(0, file.end); // seekg 函数则用于已经打开要进行输入的文件
		int length = file.tellg();
		cout << "len(file.tellg) in sendhtml: " << length << endl;
		file.seekg(0, file.beg);

		// char* buffer = new char[N];   // 此处分配内存时，容易炸，容易数组越界
		// file.read(buffer, length);
		// 读取文件
		file.read(buffer2, length);

		// cout <<"send(cfd, buffer, length, 0)=> " << send(cfd, buffer, length, 0) << endl;
		cout << "send(cfd, buffer2, length, 0)=> " << send(cfd, buffer2, length, 0) << endl;

		// string tt = "HTTP/1.1 201 OK\r\n\r\n";
		// tt += data;
		// strncpy(data,tt.c_str(),tt.length());
		// cerr << "asfhiuhsjkdhg:"<<data<<endl;

		//

		// write(cfd,data,strlen(data));
		// write(cfd, buffer, strlen(buffer));
		cerr << "######################## sendhtml end ###############################" << endl;
		fclose(pr);
	}
	else if (filepath[strlen(filepath) - 1] == 'i')
	{
		char *const arg[] = {"ls", "-l", "-a", NULL};
		// string arg[] = {"ls", "-l", "-a", NULL};
		cout << "=================  cgi  ===============" << endl;
		// execv( "time-web.cgi", arg);

		/**
		 *创建子进程并调用函数execv
		 *execv中希望接收一个以NULL结尾的字符串数组的指针
		 */
		if (fork() == 0) // 创建子进程
		{
			// in child
			printf("------------execv------------\n");
			if (execv("time-web.cgi", arg) < 0)
			{
				perror("execute error ");
				exit(1);
			}
			else
			{
				cout << "=================  execute successfully!  ===============" << endl;
			}
		}

		char sendBuf[N];
		sprintf(sendBuf, "HTTP/1.1 201 OK\nContent-Encoding:UTF-8\r\n");
		send(cfd, sendBuf, strlen(sendBuf), 0);

		ifstream file;
		// 打开文件
		file.open("time.txt", ifstream::in | ios::binary);
		// 计算长度
		file.seekg(0, file.end); // seekg 函数则用于已经打开要进行输入的文件
		int length = file.tellg();
		cout << "len(file.tellg) in sendhtml: " << length << endl;
		file.seekg(0, file.beg);

		// char* buffer = new char[N];   // 此处分配内存时，容易炸，容易数组越界
		// file.read(buffer, length);
		// 读取文件
		file.read(buffer2, length);

		// cout <<"send(cfd, buffer, length, 0)=> " << send(cfd, buffer, length, 0) << endl;
		cout << "send(cfd, buffer2, length, 0)=> " << send(cfd, buffer2, length, 0) << endl;

		cout << "=================  cgi end ! ===============";
	}
	else
	{
		cout << "sendPic ing!" << endl;
		sendPic(cfd, filepath);
		// int flen;
		// char *img = read_image(filepath, flen);
		// if (send_image(cfd, img, flen) == -1) {
		//         fprintf(stderr,"Write Error:%s\n",strerror(errno));
		//         exit(1);
		//     }
	}
}

string get_path(int cfd, string doc_root)
{
	// char buf[BUFSIZ];
	char buf[N];
	read(cfd, buf, 1024 * 10);

	// buf:   GET / HTTP/1.1 -- Host --Connection
	cout << " buf :  " << buf << endl;

	string get_current_req = ""; // get_current_req -- /_include/css/fonts/Icons.ttf
	for (int j = 4; j < N - 1; j++)
	{
		// cout << " j= "<< j << "  buf[j]  " <<  buf[j] <<endl;
		if (buf[j] != ' ')
		{
			get_current_req += buf[j];
		}
		else
			break;
	}
	if (get_current_req.length() == 1)
	{
		get_current_req = "/index.html";
	}
	cout << "get_current_req: " << get_current_req << "  len  " << get_current_req.length() << endl;
	cout << " doc_root :  " << doc_root << endl;
	cout << " \nbuf : " << buf << endl;
	string web_root = doc_root + get_current_req;
	return web_root;
}

void start_httpd(unsigned short port, string doc_root, int pol_num)
{
	cerr << "Starting server (port: " << port << ", doc_root: " << doc_root << ")" << endl;

	PORT = port;
	int sfd, cfd;

	struct sockaddr_in caddr;
	socklen_t caddr_len;
	sfd = get_sfd();
	cerr << "sfd    = " << sfd << endl;
	/*获取客户端地址结构大小*/
	caddr_len = sizeof(caddr_len);
	if (pol_num == 0)
	{
		// 一个线程进行处理
		while (1)
		{
			cerr << "111111111111111 [while] 1111111111111111111" << endl;
			cfd = accept(sfd, (struct sockaddr *)&caddr, &caddr_len); /*监听客户端链接, 会阻塞*/
			string webpath = get_path(cfd, doc_root);
			cerr << "webpath : " << webpath << endl;
			cerr << "cfd : " << cfd << endl;
			Parameter *parameter = new Parameter;
			cerr << "################ [Sigle thread] ################" << endl;
			parameter->client_socket = cfd;
			parameter->client_addr = webpath;
			cerr << "################ [Sigle thread] ################" << endl;
			cerr << "     1111 = " << parameter->client_socket << parameter->client_addr << endl;
			accept_request(parameter);
			delete (parameter);
			close(cfd);
		}
	}
	else
	{
		// 多线程处理
		threadpool_t *thp = threadpool_create(pol_num, pol_num, 100); //创建线程池
		while (1)
		{
			cfd = accept(sfd, (struct sockaddr *)&caddr, &caddr_len); /*监听客户端链接, 会阻塞*/
			cerr << "################ [Multi thread] ################ cfd:  " << cfd << endl;
			string webpath = get_path(cfd, doc_root);
			cout << "while-cfd: " << cfd << endl;
			cout << "while-webpath: " << webpath << endl;
			Parameter *parameter = new Parameter;
			parameter->client_socket = cfd;
			parameter->client_addr = webpath;

			// 需要改善
			threadpool_add_task(thp, &accept_request, parameter); /* 向线程池中添加任务 */
			delete (parameter);
		}
		sleep(10); /* 等子线程完成任务 */
		threadpool_destroy(thp);
	}
	close(sfd);
	// HTTPResponse* response=NULL;
	// HTTPRequest* request=NULL;
	// char* filename=NULL, *URL=NULL;
	// char* Message = NULL;

	// HTTPMessage* message = read_socket_into_message(buffer);
	// request = trans_message_to_request(message);
	// connection=request->connection;
	// response=construct_response(200,request,connection);
	// message2=trans_response_to_message(response);
	// size2 = strlen(message2->header);
	// write(client_socket,message2->header,size2);
	// fd = open(message2->body,O_RDONLY);
	// if(fd==-1) goto end;
	// get_content(fd,client_socket);
	// close(fd);
	// free(buffer);
	// close(cfd);
}
